﻿using System;
using System.Xml;
using XmlStudio.DataLayer;
using XmlStudio.Interfaces;
using XmlStudio.Models.Events;

namespace XmlStudio.Models.Actions {
    /// <summary>
    /// Creates root element for the document.
    /// </summary>
    internal sealed class CreateRootElementAction : XmlModelAction {
        #region Fields
        #region Private
        /// <summary>
        /// <see cref="XmlDocument"/> used for creating nodes.
        /// </summary>
        private XmlDocument document;

        /// <summary>
        /// Root element.
        /// </summary>
        private BigXmlNodeInternal root;

        /// <summary>
        /// Name of the element.
        /// </summary>
        private string name;

        /// <summary>
        /// Prefix of the element.
        /// </summary>
        private string prefix;

        /// <summary>
        /// Namespace uri of the element.
        /// </summary>
        private string namespaceUri;

        /// <summary>
        /// Indicates whether element is being created with namespace.
        /// </summary>
        private bool withNamespace = false;
        #endregion
        #endregion

        #region Constructors
        #region Public
        /// <summary>
        /// Initializes a new instance of the <see cref="CreateRootElementAction"/> class.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="name">Name of the root element.</param>
        public CreateRootElementAction(IXmlModelWithNotifications model, string name)
            : base(model) {
            this.document = model.XmlDocument;
            this.name = name;
            this.withNamespace = false;
            this.address = new int[] { 1 };

            CreateRoot();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateRootElementAction"/> class.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="prefix">Element's prefix.</param>
        /// <param name="localName">Element's local name.</param>
        /// <param name="namespaceUri">Element's namespace uri.</param>
        public CreateRootElementAction(IXmlModelWithNotifications model, string prefix, string localName, string namespaceUri)
            : base(model) {
            this.model = model;
            this.document = model.XmlDocument;
            this.name = localName;
            this.prefix = prefix;
            this.namespaceUri = namespaceUri;
            this.withNamespace = true;
            this.address = new int[] { 1 };

            CreateRoot();
        }

        private void CreateRoot() {
            XmlElement element = null;
            if(withNamespace) {
                element = this.document.CreateElement(this.prefix, this.name, this.namespaceUri);
            } else {
                element = this.document.CreateElement(this.name);
            }

            this.root = BigXmlNodeInternal.Create(element, this.address, null);
            this.root.SetModified(true);
        }
        #endregion
        #endregion

        #region Methods
        #region Protected
        /// <summary>
        /// Executes the action.
        /// </summary>
        protected override void ExecuteCore() {
            if(this.model.Root != null) {
                throw new ApplicationException("Cannot execute when there is a root element. Remove old root element in order to perform this action.");
            } else {
                this.model.Root = this.root;
                this.model.AddNode(root.XmlNode, root);

                this.model.SetModified(true);
                this.model.OnRootElementAdded(new RootElementAddedEventArgs<BigXmlNode>(this.root));
                this.model.OnRawXmlChanged(new RawXmlChangedEventArgs());
                this.model.TotalNodeCount++;
            }
        }

        /// <summary>
        /// Executes the undo for this action.
        /// </summary>
        protected override void UnExecuteCore() {
            this.model.RemoveNode(this.root.XmlNode);
            this.model.RemoveRootNode();
            this.model.Root = null;

            this.model.SetModified(this.modelWasModified);
            this.model.OnNodeRemoved(new NodeRemovedEventArgs<BigXmlNode>(this.root));
            this.model.OnRawXmlChanged(new RawXmlChangedEventArgs());
            this.model.TotalNodeCount--;
        }
        #endregion
        #endregion
    }
}